package com.meshnetics.gb.type.impl.zcl;

/**
 * WRITE DESCRIPTION HERE
 *
 * @author AKovylin
 */
public class Bitmap extends FixedBytesDataValue {
    private static final int BITS_IN_BYTE = FixedBytesDataType.BITS_IN_BYTE;

    // write comment - constructor only for extension, no checks or cloning performed!
    // (to avoid double checks/cloning, checks/cloning must be done by type on deserialization)
    protected Bitmap(TypeBitmap type, byte[] value) {
        super(type, value);
    }

    public boolean isInvalid() {
        return false; // always valid
    }

    protected boolean isUnsigned() {
        return true;
    }

    public boolean getBit(int bit) {
        checkBitInRange(bit);
        return getBit0(bit);
    }

    private boolean getBit0(int bit) {
        // need to count bytes from last (rightmost) one !!!
        return (value[value.length - 1 - bit / BITS_IN_BYTE] & (1 << (bit % BITS_IN_BYTE))) > 0;
    }

    protected void setBit(int bit, boolean on) {
        checkBitInRange(bit);
        setBit0(bit, on);
    }

    private void setBit0(int bit, boolean on) {
        // need to count bytes from last (rightmost) one !!!
        if (on != getBit0(bit)) {
            if (on) { // bit is off, just add
                value[value.length - 1 - bit / BITS_IN_BYTE] += (1 << (bit % BITS_IN_BYTE));
            } else { // bit is on, just substract
                value[value.length - 1 - bit / BITS_IN_BYTE] -= (1 << (bit % BITS_IN_BYTE));
            }
        }
    }

    private void checkBitInRange(int bit) {
        if (bit < 0 || bit >= value.length * BITS_IN_BYTE) {
            throw new IllegalArgumentException("out of range");
        }
    }

    public int getBits(int fromBit, int toBit) {
        checkBitsRange(fromBit, toBit);
        int res = 0;
        for (int i = toBit - 1; i > fromBit; i--) {
            res += getBit0(i) ? 1 : 0;
            res = res << 1;
        }
        res += getBit0(fromBit) ? 1 : 0;
        return res;
    }

    protected void setBits(int fromBit, int toBit, int bits) {
        checkBitsRange(fromBit, toBit);
        for (int i = fromBit; i < toBit; i++) {
            setBit0(i, (bits & 0x01) > 0);
            bits = bits >>> 1;
        }
    }

    private void checkBitsRange(int fromBit, int toBit) {
        checkBitInRange(fromBit);
        if (toBit <= fromBit || toBit > value.length * BITS_IN_BYTE) {
            throw new IllegalArgumentException("invalid bits range");
        }
        if (toBit - fromBit > 32) { // int size
            throw new IllegalArgumentException("bits range is too long, 32 bits max allowed");
        }
    }
}
